iT邦幫忙

2024 iThome 鐵人賽

DAY 4
0
生成式 AI

Semantic Kernel 的魔力-用.NET探索生成式應用系列 第 4

建立 Kernel 不同的寫法與 Role Prompt 的實現

  • 分享至 

  • xImage
  •  

在上一篇文章,使用一個入門範例示範如何建立 Kernel 以及與 LLMs 進行對話,在那個範例裡並沒有涉及到使用 OpenAI GPT 模型常用的 System Prompt、User Prompt,今天的內容就來看看如何建立一個具有 System Prompt、User Prompt 的對話,以及探討建立 Kernel的不同寫法。

建立 Kernel 物件的不同寫法

在開始之前,先談一下建立 Kernel 的變化寫法,回顧一下前一篇文章的範例是這樣建立的,在建立 Kernel 物件後,調用 AddOpenAIChatCompletion 方法。

 Kernel kernel = Kernel.CreateBuilder()
            .AddOpenAIChatCompletion(
            modelId: Config.openai_modelId,
            apiKey: Config.openai_apiKey)
            .Build();

此外,另一種可行的方式是先建立 builder,再透過公開的 Services 屬性來新增服務。這種方法的彈性在於,當應用程式需要根據不同的功能情境來配置不同服務時,能夠提供更好的靈活性,產生更符合情境所需要的 Kernel 物件 。

var builder = Kernel.CreateBuilder();
builder.Services.AddAzureOpenAIChatCompletion(
    endpoint: Config.aoai_endpoint,
    deploymentName: Config.aoai_deployment,
    apiKey: Config.aoai_apiKey);
var kernel = builder.Build();

ChatCompletionService 與 ChatHistory 體實現 Role Prompt 的機制與短期對話記錄的記憶

如果仔細研究 OpenAI API,你會發現,在 API 的請求(Request)和回應(Response)中,Message 物件會包含一個 role 屬性,而它的值可以是 system、user、assistant(在新版本中還新增了 tool,但這裡先不討論)。

以下是各個 role 的說明:

  • user: 顧名思義,這是使用者傳遞的訊息。通常,我們會使用 user 這個角色來發送 prompt 給 OpenAI。
  • system: 這個角色類似於系統設定的 prompt。常見的使用情境是我們賦予 OpenAI 某個角色或行為指令,這些可以放在 system 角色的 prompt 中。
  • assistant: 這個角色代表大型語言模型(LLM)的回應。因此,當一則訊息的 role 是 assistant,代表這是 AI 生成的內容。

那麼當我們想依循具有 role 機制的方式建立對話內容時,應該怎麼寫?在 kernel.InvokePromptAsync 置入特定格式即可。

Kernel kernel = Kernel.CreateBuilder()
                .AddAzureOpenAIChatCompletion(
                    endpoint: Config.aoai_endpoint,
                    deploymentName: Config.aoai_deployment,
                    apiKey: Config.aoai_apiKey)
                .Build();
string chatPrompt = """
         <message role="system">你是一位專業的C#程式專家,協助我進行C#程式的開發工作</message>
         <message role="user">你能說說什麼是非同步方法嗎</message>
         """;
Console.WriteLine(await kernel.InvokePromptAsync(chatPrompt));                

但這樣的寫法讓 Prompt 變的有些凌亂,我們進一步改用以下的寫法

Kernel kernel = Kernel.CreateBuilder()
                .AddAzureOpenAIChatCompletion(
                    endpoint: Config.aoai_endpoint,
                    deploymentName: Config.aoai_deployment,
                    apiKey: Config.aoai_apiKey)
                .Build();
ChatHistory chatHistory = [];
var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
chatHistory.AddSystemMessage("你是一位專業的C#程式專家,協助我進行C#程式的開發工作");
chatHistory.AddUserMessage("你能說說什麼是非同步方法嗎");
Console.WriteLine(await chatCompletionService.GetChatMessageContentAsync(chatHistory: chatHistory, kernel: kernel));            

這種寫法先建立 ChatHistory 物件,用於收集連續對話的記錄,作用於實現短期對話記錄的記憶機制,接著透過 GetRequiredService 方法取得 ChatCompletionService 物件。根據 Prompt 的內容,使用 AddSystemMessage 和 AddUserMessage 方法將訊息加入對話記錄(ChatHistory)。最後,再透過 ChatCompletionService 物件的 GetChatMessageContentAsync 方法與 OpenAI 模型進行互動。相比之下,這種寫法比使用 kernel.InvokePromptAsync 更加清晰。

此外這個寫法,也許可能會有個疑惑是 GetRequiredService,在上述程式中 kernel 物件的建立並沒有加入 Service 啊,那為什麼 GetRequiredService 卻可以取得 IChatCompletionService 的實作呢?其實在建立 Kernel 物件時 AddAzureOpenAIChatCompletion 這個方法的內部,做了這件事

Func<IServiceProvider, object?, AzureOpenAIChatCompletionService> factory = (serviceProvider, _) =>
{
       AzureOpenAIClient client = CreateAzureOpenAIClient(
            endpoint,
            new AzureKeyCredential(apiKey),
            HttpClientProvider.GetHttpClient(httpClient, serviceProvider));

       return new(deploymentName, client, modelId, serviceProvider.GetService<ILoggerFactory>());
};

builder.Services.AddKeyedSingleton<IChatCompletionService>(serviceId, factory);        

也就是會根據 AddAzureOpenAIChatCompletion 的情況,在內部自動加入了 AzureOpenAIChatCompletionService 服務, 而 AzureOpenAIChatCompletionService 就是 IChatCompletionService 的實作類別。

結語

建立 Kernel 大致可以分成 2 種方式,方式1 是一次就配置好,適合不需要依情境條件而產生 kernel 物件的場景,相對的方式2 提供了可以依情境條件而產生 kernel 物件的場景,開發者可以依情況做不同的選擇。此外 ChatCompletionService 搭配 ChatHistory 可以具體實現 OpenAI API中關於 Role Prompt 的機制以及短期對話記錄的記憶。

  • 方式 1
Kernel kernel = Kernel.CreateBuilder()
            .AddAzureOpenAIChatCompletion(
            endpoint: Config.aoai_endpoint,
            deploymentName: Config.aoai_deployment,
            apiKey: Config.aoai_apiKey)
            .Build();      
  • 方式 2
var builder = Kernel.CreateBuilder();

if (config.IsAzureOpenAIConfigured)
{
    // Use Azure OpenAI Deployments
    builder.Services.AddAzureOpenAIChatCompletion(
        config.AzureOpenAI!.DeploymentName!,
        config.AzureOpenAI.Endpoint!,
        config.AzureOpenAI.ApiKey!);
}
else
{
    // Use OpenAI
    builder.Services.AddOpenAIChatCompletion(
        config.OpenAI!.ModelId!,
        config.OpenAI.ApiKey!,
        config.OpenAI.OrgId);
}

var kernel = builder.Build();     

上一篇
認識 Kernel
下一篇
Plugin 的魔力 — 第一次見面
系列文
Semantic Kernel 的魔力-用.NET探索生成式應用30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言